package mech;

import java.util.ArrayList;

import obj.BossEnemy;
import obj.Enemy;
import obj.MovingPlatform;
import obj.Platform;
import obj.Player;
import obj.Projectile;
import obj.ShootingEnemy;
import obj.Sprite;

import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.state.StateBasedGame;

import play.Things;
import util.Rect;
import util.Settings;

/**
 * Class of useful methods that are used among several other classes.
 * @author VincentLy
 */
public class UsefulMethods{
	
	/**
	 * Sets the clip for the world screen.
	 * @param g - Graphics
	 * @author VincentLy
	 */
	public static void map(Graphics g){g.setWorldClip(-Settings.tx,-Settings.ty,Settings.screenX,Settings.screenY);}
	
	/**
	 * Scrolls the screen accordingly with the player.
	 * @param r - the scene rectangle
	 * @param p - the player
	 * @return the translations
	 * @author VincentLy
	 */
	public static float[] sceneChange(Rect r,Player p){
		if(!r.contains(p)){
			float xo = r.left();
			float yo = r.top();
			if(p.left()<r.left())
				r.setLocation(p.left(),r.top());	
			if(p.right()>r.right())
				r.setLocation(p.right()-r.getWidth(),r.top());
			if(p.top()<r.top())
				r.setLocation(r.left(),p.top());
			if(p.bottom()>r.bottom())
				r.setLocation(r.left(),p.bottom()-r.getHeight());  
			return new float[]{xo - r.left(),yo - r.top()};
		}   
		return new float[]{0,0};
	}
	
	/**
	 * Checks for bullet collisions with player, enemies, and platforms.
	 * @param gc - GameContainer
	 * @author VincentLy
	 */
	public static void bulletCollisions(GameContainer gc,StateBasedGame sbg){
		ArrayList<Projectile> hits = new ArrayList<Projectile>();
		ArrayList<Enemy> dead = new ArrayList<Enemy>();
		for(Projectile p:Things.projectiles){
			//checks if projectie belongs to an enemy
			if(p.owner()!=Things.player && !Things.player.isInvincible() && p.intersects(Things.player)){
				//apply damage to the player
				Things.player.setYVel(30);
				Things.player.setHit(true);
				Things.player.setHP(Things.player.hp()-p.dmg());
				//remove the projectile
				hits.add(p);
				//check if player died
				if(Things.player.hp()<=0){
					Things.player.setHP(500);
					sbg.enterState(Settings.goID);
				}	
				//give player invincibility
				Things.player.invincible();
			}
			else
				for(Enemy e:Things.enemies)
					//checks if the projectile belongs to the player
					if(p.owner()==Things.player && p.intersects(e)){
						if(!(e instanceof ShootingEnemy || e instanceof BossEnemy))
							e.setHit(true);
						p.setUses(p.uses()-1);
						//remove the projectile
						if(p.uses()<=0)
							hits.add(p);
						//apply damage to the enemy
						e.setHP(e.hp()-p.dmg());
						//check if enemy dead
						if(e.hp()<=0)
							dead.add(e);
						else{
							e.setYVel(30);
							e.setXVel(p.dir()*10);
							e.setPlatform(null);
						}	
						break;
					}		
		}	
		//remove projectiles
		for(Projectile p:hits)
			Things.projectiles.remove(p);
		hits.clear();
		//remove enemies
		for(Enemy e:dead){
			if(Things.player.hp()+10>500)
				Things.player.setHP(500);
			else
				Things.player.setHP(Things.player.hp()+10);
			Things.enemies.remove(e);
			if(e instanceof BossEnemy)
				((BossEnemy)e).clean();
			else if(e instanceof ShootingEnemy)
				((ShootingEnemy)e).clean();
		}	
		//remove projectiles that collided with platforms
		for(Projectile p:Things.projectiles){
			for(Platform pl:Things.platforms)
				if(p.intersects(pl) && (!p.trans() || !pl.isTrans()))
					hits.add(p);
		}	
		for(Projectile p:hits)
			Things.projectiles.remove(p);
	}
	
	/**
	 * Checks for collisions between a sprite and platforms.
	 * @param s - the sprite
	 * @author VincentLy
	 */
	public static void platformCollisions(Sprite s){
		ArrayList<Platform> conflicts = new ArrayList<Platform>();
		//check which platforms are in question of collision
		for(Platform p:Things.platforms)
			if(p.intersects(s))
				conflicts.add(p);  
		//notes the sprite is airborne if no collisions
		if(s.yvel()!=0 && conflicts.isEmpty() && s.jumps()>0){	  
			s.setPlatform(null);
			s.setJumps(1);
		}	
		for(Platform p:conflicts){  
			boolean falling = false;
			boolean wasAbovePlatform = false;
			boolean wasBelowPlatform = false; 
			//notes if sprite is already on a platform
			if(s.platform()!=null && s.platform() == p){
				//stop and reallocate sprite accordingly
				s.setYVel(0);
				s.move(s.x(),p.top()-s.getHeight()/2);
				s.setPlatform(p);
				s.setJumps(2);	
				s.setHit(false);
				if(s instanceof Player)
					((Player) s).land();
			}
			else{
				//checks if sprite is falling
				if(s.yvel()<0)
					falling = true;
				//checks if sprite was previously above the platform
				if(p instanceof MovingPlatform){
					if(s.yPrev()+s.height()/2<((MovingPlatform)p).yprev()-((MovingPlatform)p).height()/2)
						wasAbovePlatform = true;
				}
				else if(s.yPrev()+s.height()/2<p.top())
					wasAbovePlatform = true;
				//checks if sprite was previously below platform
				if(s.yPrev()-s.height()/2>p.bottom())
					wasBelowPlatform = true;   
				//handles falling onto a platform
				if(falling && wasAbovePlatform){     	
					//stop and reallocate sprite accordingly
					s.setYVel(0);					
					s.move(s.x(),p.top()-s.getHeight()/2);
					s.setPlatform(p);
					s.setJumps(2);
					s.setHit(false);
					if(s instanceof Player)
						((Player) s).land();
				}
				else if(!p.isTrans()){
					//handles hitting opaque platform from bottom
					if(!falling && wasBelowPlatform){
						//stop and reallocate sprite
						s.setYVel(0);
						s.move(s.x(),p.bottom()+s.getHeight()/2+1);    			
					}
					else{    	
						//handles hitting opaque platform from left or right
						if(s.x()<p.x() && s.right()>p.left())   				
							s.move(p.left()-s.getWidth()/2,s.y());	    				
						else if(s.x()>p.x() && s.left()<p.right())
							s.move(p.right()+s.getWidth()/2,s.y());	  
					}
				}
			}
		}
		//clear the conflicts
		s.update2();
		conflicts.clear();
	}
	
	/**
	 * Checks for enemy collisions with the player.
	 * @param p - the player
	 * @param sbg - StateBasedGame
	 * @author VincentLy
	 */
	public static void enemyCollisions(Player p,StateBasedGame sbg){
		if(!p.isInvincible())
			for(Enemy e:Things.enemies){
				//handles contact damage from player on enemy
				if(p.intersects(e)){
					//apply damage to the player
					p.setYVel(30);
					p.setXVel(-p.dir()*30);
					p.setHit(true);
					p.setHP(p.hp()-50);
					//check if player died
					if(p.hp()<=0){
						p.setHP(500);
						sbg.enterState(Settings.goID);
					}	
					//give player invincibility
					p.invincible();
					break;
				}
			}		
	}
}